from rdflib import Namespace, BNode, RDF, Literal, RDFS
from rdflib.Graph import Graph

from common import UCI_NS, AGENT_NS, ECP_NS
from urllib2 import HTTPError
import time
import urllib
import urllib2
import simplejson
import urlparse

CLOUDS_NS = Namespace("urn:clouds:")
MACHINES_NS = Namespace("urn:machines:")
PACKAGE_NS = Namespace("urn:packages:")


class Cloud(object):
    def __init__(self, ecp_base):
        self.ecp_base = ecp_base
        
    
    @property
    def uriref(self):
        return CLOUDS_NS['ecp_cloud']
    
    def serialize_to(self, graph):
        graph.add((self.uriref, RDF.type, ECP_NS['ECPCloud']))
        graph.add((self.uriref, RDFS.label, Literal("ECP instance at %s" % self.ecp_base)))

class Machine(object):
    def __init__(self, plugin, cloud, id):
        self.cloud = cloud
        self.plugin = plugin
        self.id = id
         
        self.name = None
        
        self.is_running = False
        self.is_stopped = False

    def reload(self):        
        json_response = self.plugin.request('rest/machine/%s/' % self.id).read()
        response = simplejson.loads(json_response)
        
        errno = int(response['errno'])
        if errno == 0:
            m = response['machine']
            info = response['info']
            
            self.name = m['machine_name']
            
            if info[0] in (1, 2):
                self.is_running = True
                self.is_stopped = False
#            elif info[0] in (0, 3, 4, 5, 6, 7):
            else:
                self.is_running = False
                self.is_stopped = True
#            else:
#                self.is_running = False
#                self.is_stopped = False
                
            self.hypervisors = response['hypervisors']

    @property
    def uriref(self):
        return MACHINES_NS[self.id]
        
    def serialize_to(self, graph):
         
        if self.hypervisors:
            graph.add((self.uriref, RDF.type, ECP_NS['ECPServer']))
        else:
            graph.add((self.uriref, RDF.type, ECP_NS['ECPVirtualMachine']))
            
        graph.add((self.uriref, UCI_NS['belongs_to_cloud'], self.cloud.uriref))
        
        if self.name:
            graph.add((self.uriref, RDFS.label, Literal(self.name)))
        
        if self.is_running:
            graph.add((self.uriref, UCI_NS['machine_state'], UCI_NS['machine_running']))
        elif self.is_stopped:
            graph.add((self.uriref, UCI_NS['machine_state'], UCI_NS['machine_stopped']))

class Package(object):
    def __init__(self, cloud, id, name):
        self.id = id
        self.name = name
        self.cloud = cloud
        
    @property
    def uriref(self):
        return PACKAGE_NS[self.id]
        
    def __unicode__(self):
        return unicode(self.uriref)
        
    def serialize_to(self, graph):
        graph.add((self.uriref, RDF.type, UCI_NS['VirtualMachinePackage']))
        graph.add((self.uriref, RDFS.label, Literal(self.name)))
        graph.add((self.uriref, UCI_NS['belongs_to_cloud'], self.cloud.uriref))

class Plugin(object):
    """ECP plugin"""
    def __init__(self, config, section):
        self.ecp_base = config.get(section, 'ecp_base')
        self.ecp_user = config.get(section, 'ecp_user')
        self.ecp_password = config.get(section, 'ecp_password')
        self.default_cluster_uuid = config.get(section, 'default_cluster_uuid')
        
        self.cloud = Cloud(self.ecp_base)
        self.machines = []
        self.packages = []
        self.uriref_map = {}
        
    def initialize(self):
        json_response = self.request('rest/machines/').read()
        response = simplejson.loads(json_response)
        for mid in response['machines']:
            m = Machine(self, self.cloud, mid)
            m.reload()
            self.machines.append(m)         
        
        
        # fetch packages
        json_response = self.request('rest/valet/packages/').read()
        response = simplejson.loads(json_response)
        packages = response['packages']
        
        for uuid, name in packages:
            self.packages.append(Package(self.cloud, uuid, name))    

        self.build_uriref_map()

    def build_uriref_map(self):
        self.uriref_map = {}
        self.uriref_map[self.cloud.uriref]=self.cloud
        for m in self.machines:
            self.uriref_map[m.uriref]=m
        for p in self.packages:
            self.uriref_map[p.uriref]=p
            
    def is_uri_from_this_plugin(self, uriref):
        return uriref in self.uriref_map
        
    def get_state(self, req_id, graph):
        g = Graph()
        self.cloud.serialize_to(g)
        for m in self.machines:
            m.reload()
            m.serialize_to(g)
        for p in self.packages:
            p.serialize_to(g)
        return g
    
    def start_instance(self, req_id, graph):
        machine_id = graph.value(subject=req_id, predicate=AGENT_NS['virtual_machine'])
        
        machine = self.uriref_map[machine_id]
        assert(isinstance(machine, Machine))
        
        print "Starting %s (%s)" % (machine_id, machine.id)
        
        ecp_uri = urlparse.urljoin(self.ecp_base, 'rest/machine/%s/actions/create/' % machine.id)
        
        json_response = self._ecp_request(ecp_uri, '').read()
        response = simplejson.loads(json_response)
        
        print "got ECP response: %s" % response

        machine.reload()
        
        g = Graph()
        machine.serialize_to(g)
        return g  
        
    def stop_instance(self, req_id, graph):
        machine_id = graph.value(subject=req_id, predicate=AGENT_NS['virtual_machine'])
        
        machine = self.uriref_map[machine_id]
        assert(isinstance(machine, Machine))
        
        print "Stopping %s (%s)" % (machine_id, machine.id)
        
        ecp_uri = urlparse.urljoin(self.ecp_base, 'rest/machine/%s/actions/shutdown/' % machine.id)
        print ecp_uri
        json_response = self._ecp_request(ecp_uri, '').read()
        response = simplejson.loads(json_response)
        
        print "got ECP response: %s" % response
        machine.reload()
        
        g = Graph()
        machine.serialize_to(g)
        return g  
        
    def provision(self, req_id, graph):
        package_uri = graph.value(req_id, AGENT_NS['package'])
        package = self.uriref_map[package_uri]
        
        assert(isinstance(package, Package))
        
        data = dict(pkguuid=package.id,
                    join_cluster=self.default_cluster_uuid,
                    target_cluster=self.default_cluster_uuid,
                    num_machines=1)
        print data
        json_response = self._ecp_request(urlparse.urljoin(self.ecp_base, 'rest/valet/'), urllib.urlencode(data)).read()
        response = simplejson.loads(json_response)
        print json_response
        results = response['results']
        txid = results[0]['txid']
        newuuid = results[0]['newuuid']
        
        newmachine = Machine(self, self.cloud, newuuid)
        
        i = 0
        while 1:
            json_response = self._ecp_request(urlparse.urljoin(self.ecp_base, "rest/transaction/%s/" % str(txid))).read()
            response = simplejson.loads(json_response)
            
            t = response['transaction']
            #t['uuid'], t['target'], t['start_time'], int(t['completion']), t['message'], data=t.get('data', None)
            if int(t['completion']) > 99:
                newmachine.reload()
                break;
            time.sleep(5)
            i+=1
            
            if i>100:
                # if it takes too long to provision -- spit current state
                newmachine.reload()
                break
            
        self.machines.append(newmachine)
        
        self.build_uriref_map()
            
        g = Graph()
        newmachine.serialize_to(g)
        return g
        
    def destroy(self, req_id, graph):
        machine_id = graph.value(subject=req_id, predicate=AGENT_NS['virtual_machine'])
        
        machine = self.uriref_map[machine_id]
        assert(isinstance(machine, Machine))
        print "Destroying %s (%s)" % (machine_id, machine.id)
        
        ecp_uri = urlparse.urljoin(self.ecp_base, 'rest/machine/%s/actions/totaldelete/' % machine.id)
        json_response = self._ecp_request(ecp_uri, '').read()
        response = simplejson.loads(json_response)
        
        print "got ECP response: %s" % response
        
        self.machines.remove(machine)
        
        g = Graph()
        return g   

    def request(self, resource, postdata=None):
        ecp_uri = urlparse.urljoin(self.ecp_base, resource)
        return self._ecp_request(ecp_uri, postdata)
    
    def _ecp_request(self, ecp_uri, postdata=None):
#        import httplib
#        headers = {'Authorization': self._basic_auth_header(self.client.ecp_user, self.client.ecp_password)}
        auth_handler = urllib2.HTTPBasicAuthHandler()
        auth_handler.add_password(realm='Enomalism2', uri=ecp_uri, user=self.ecp_user, passwd=self.ecp_password)
        opener = urllib2.build_opener(auth_handler)
        
        try:
            return opener.open(ecp_uri, data=postdata)
        except HTTPError, e:
            print "Got HTTPError %s (%s)" % (e, e.code)
            if 200 <= e.code < 300:
                return e
            else:
                raise
#
#    def _basic_auth_header(self, username, password):
#        import base64
#
#        hash = base64.encodestring('%s:%s' % (username, password)).rstrip()
#        return 'Basic %s' % hash

